1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * Copyright 2011, Richard Lowe 29 */ 30 31 /* Functions in this file are duplicated in locallibm.il. Keep them in sync */ 32 33 #ifndef _LIBM_INLINES_H 34 #define _LIBM_INLINES_H 35 36 #ifdef __GNUC__ 37 #ifdef __cplusplus 38 extern "C" { 39 #endif 40 41 #include <sys/types.h> 42 #include <sys/ieeefp.h> 43 44 #define _LO_WORD(x) ((uint32_t *)&x)[0] 45 #define _HI_WORD(x) ((uint32_t *)&x)[1] 46 #define _HIER_WORD(x) ((uint32_t *)&x)[2] 47 48 extern __GNU_INLINE double 49 __inline_sqrt(double a) 50 { 51 double ret; 52 53 __asm__ __volatile__("fsqrt\n\t" : "=t" (ret) : "0" (a) : "cc"); 54 return (ret); 55 } 56 57 extern __GNU_INLINE double 58 __ieee754_sqrt(double a) 59 { 60 return (__inline_sqrt(a)); 61 } 62 63 extern __GNU_INLINE float 64 __inline_sqrtf(float a) 65 { 66 float ret; 67 68 __asm__ __volatile__("fsqrt\n\t" : "=t" (ret) : "0" (a) : "cc"); 69 return (ret); 70 } 71 72 extern __GNU_INLINE double 73 __inline_rint(double a) 74 { 75 __asm__ __volatile__( 76 "andl $0x7fffffff,%1\n\t" 77 "cmpl $0x43300000,%1\n\t" 78 "jae 1f\n\t" 79 "frndint\n\t" 80 "1: fwait\n\t" 81 : "+t" (a), "+&r" (_HI_WORD(a)) 82 : 83 : "cc"); 84 85 return (a); 86 } 87 88 /* 89 * 00 - 24 bits 90 * 01 - reserved 91 * 10 - 53 bits 92 * 11 - 64 bits 93 */ 94 extern __GNU_INLINE int 95 __swapRP(int i) 96 { 97 int ret; 98 uint16_t cw; 99 100 __asm__ __volatile__("fstcw %0\n\t" : "=m" (cw)); 101 102 ret = (cw >> 8) & 0x3; 103 cw = (cw & 0xfcff) | ((i & 0x3) << 8); 104 105 __asm__ __volatile__("fldcw %0\n\t" : : "m" (cw)); 106 107 return (ret); 108 } 109 110 /* 111 * 00 - Round to nearest, with even preferred 112 * 01 - Round down 113 * 10 - Round up 114 * 11 - Chop 115 */ 116 extern __GNU_INLINE enum fp_direction_type 117 __swap87RD(enum fp_direction_type i) 118 { 119 int ret; 120 uint16_t cw; 121 122 __asm__ __volatile__("fstcw %0\n\t" : "=m" (cw)); 123 124 ret = (cw >> 10) & 0x3; 125 cw = (cw & 0xf3ff) | ((i & 0x3) << 10); 126 127 __asm__ __volatile__("fldcw %0\n\t" : : "m" (cw)); 128 129 return (ret); 130 } 131 132 extern __GNU_INLINE double 133 ceil(double d) 134 { 135 /* 136 * Let's set a Rounding Control (RC) bits from x87 FPU Control Word 137 * to fp_positive and save old bits in rd. 138 */ 139 short rd = __swap87RD(fp_positive); 140 141 /* 142 * The FRNDINT instruction returns a floating-point value that is the 143 * integral value closest to the source value in the direction of the 144 * rounding mode specified in the RC field of the x87 FPU control word. 145 * 146 * Rounds the source value in the ST(0) register to the nearest 147 * integral value, depending on the current rounding mode 148 * (setting of the RC field of the FPU control word), 149 * and stores the result in ST(0). 150 */ 151 __asm__ __volatile__("frndint" : "+t" (d) : : "cc"); 152 153 /* restore old RC bits */ 154 __swap87RD(rd); 155 156 return (d); 157 } 158 159 extern __GNU_INLINE double 160 copysign(double d1, double d2) 161 { 162 __asm__ __volatile__( 163 "andl $0x7fffffff,%0\n\t" /* %0 <-- hi_32(abs(d)) */ 164 "andl $0x80000000,%1\n\t" /* %1[31] <-- sign_bit(d2) */ 165 "orl %1,%0\n\t" /* %0 <-- hi_32(copysign(x,y)) */ 166 : "+&r" (_HI_WORD(d1)), "+r" (_HI_WORD(d2)) 167 : 168 : "cc"); 169 170 return (d1); 171 } 172 173 extern __GNU_INLINE double 174 fabs(double d) 175 { 176 __asm__ __volatile__("fabs\n\t" : "+t" (d) : : "cc"); 177 return (d); 178 } 179 180 extern __GNU_INLINE float 181 fabsf(float d) 182 { 183 __asm__ __volatile__("fabs\n\t" : "+t" (d) : : "cc"); 184 return (d); 185 } 186 187 extern __GNU_INLINE long double 188 fabsl(long double d) 189 { 190 __asm__ __volatile__("fabs\n\t" : "+t" (d) : : "cc"); 191 return (d); 192 } 193 194 extern __GNU_INLINE int 195 finite(double d) 196 { 197 int ret = _HI_WORD(d); 198 199 __asm__ __volatile__( 200 "notl %0\n\t" 201 "andl $0x7ff00000,%0\n\t" 202 "negl %0\n\t" 203 "shrl $31,%0\n\t" 204 : "+r" (ret) 205 : 206 : "cc"); 207 return (ret); 208 } 209 210 extern __GNU_INLINE double 211 floor(double d) 212 { 213 short rd = __swap87RD(fp_negative); 214 215 __asm__ __volatile__("frndint" : "+t" (d), "+r" (rd) : : "cc"); 216 __swap87RD(rd); 217 218 return (d); 219 } 220 221 /* 222 * branchless __isnan 223 * ((0x7ff00000-[((lx|-lx)>>31)&1]|ahx)>>31)&1 = 1 iff x is NaN 224 */ 225 extern __GNU_INLINE int 226 isnan(double d) 227 { 228 int ret; 229 230 __asm__ __volatile__( 231 "movl %1,%%ecx\n\t" 232 "negl %%ecx\n\t" /* ecx <-- -lo_32(x) */ 233 "orl %%ecx,%1\n\t" 234 "shrl $31,%1\n\t" /* 1 iff lx != 0 */ 235 "andl $0x7fffffff,%2\n\t" /* ecx <-- hi_32(abs(x)) */ 236 "orl %2,%1\n\t" 237 "subl $0x7ff00000,%1\n\t" 238 "negl %1\n\t" 239 "shrl $31,%1\n\t" 240 : "=r" (ret) 241 : "0" (_HI_WORD(d)), "r" (_LO_WORD(d)) 242 : "ecx"); 243 244 return (ret); 245 } 246 247 extern __GNU_INLINE int 248 isnanf(float f) 249 { 250 __asm__ __volatile__( 251 "andl $0x7fffffff,%0\n\t" 252 "negl %0\n\t" 253 "addl $0x7f800000,%0\n\t" 254 "shrl $31,%0\n\t" 255 : "+r" (f) 256 : 257 : "cc"); 258 259 return (f); 260 } 261 262 extern __GNU_INLINE double 263 rint(double a) 264 { 265 return (__inline_rint(a)); 266 } 267 268 extern __GNU_INLINE double 269 scalbn(double d, int n) 270 { 271 double dummy; 272 273 __asm__ __volatile__( 274 "fildl %2\n\t" /* Convert N to extended */ 275 "fxch\n\t" 276 "fscale\n\t" 277 : "+t" (d), "=u" (dummy) 278 : "m" (n) 279 : "cc"); 280 281 return (d); 282 } 283 284 extern __GNU_INLINE int 285 signbit(double d) 286 { 287 return (_HI_WORD(d) >> 31); 288 } 289 290 extern __GNU_INLINE int 291 signbitf(float f) 292 { 293 return ((*(uint32_t *)&f) >> 31); 294 } 295 296 extern __GNU_INLINE double 297 sqrt(double d) 298 { 299 return (__inline_sqrt(d)); 300 } 301 302 extern __GNU_INLINE float 303 sqrtf(float f) 304 { 305 return (__inline_sqrtf(f)); 306 } 307 308 extern __GNU_INLINE long double 309 sqrtl(long double ld) 310 { 311 __asm__ __volatile__("fsqrt" : "+t" (ld) : : "cc"); 312 return (ld); 313 } 314 315 extern __GNU_INLINE int 316 isnanl(long double ld) 317 { 318 int ret = _HIER_WORD(ld); 319 320 __asm__ __volatile__( 321 "andl $0x00007fff,%0\n\t" 322 "jz 1f\n\t" /* jump if exp is all 0 */ 323 "xorl $0x00007fff,%0\n\t" 324 "jz 2f\n\t" /* jump if exp is all 1 */ 325 "testl $0x80000000,%1\n\t" 326 "jz 3f\n\t" /* jump if leading bit is 0 */ 327 "xorl %0,%0\n\t" 328 "jmp 1f\n\t" 329 "2:\n\t" /* note that %0 = 0 from before */ 330 "cmpl $0x80000000,%1\n\t" /* what is first half of significand? */ 331 "jnz 3f\n\t" /* jump if not equal to 0x80000000 */ 332 "testl $0xffffffff,%2\n\t" /* is second half of significand 0? */ 333 "jnz 3f\n\t" /* jump if not equal to 0 */ 334 "jmp 1f\n\t" 335 "3:\n\t" 336 "movl $1,%0\n\t" 337 "1:\n\t" 338 : "+&r" (ret) 339 : "r" (_HI_WORD(ld)), "r" (_LO_WORD(ld)) 340 : "cc"); 341 342 return (ret); 343 } 344 345 #ifdef __cplusplus 346 } 347 #endif 348 #endif /* __GNUC__ */ 349 #endif /* _LIBM_INLINES_H */