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 */